import { Node, Prefab, SpriteFrame, clamp } from "cc";
import Singleton from "../Base/Singleton";
import { EntityTypeEnum, IBullet, IClientInput, InputTypeEnum, IRoom, IState, toFixed } from "../Common";
import { ActorManager } from "../Entity/Actor/ActorManager";
import { BulletManager } from "../Entity/Bullet/BulletManager";
import { EventEnum } from "../Enum";
import { JoyStickManager } from "../UI/JoyStickManager";
import EventManager from "./EventManager";

const PLAYER_SPEED = 100;
const BULLET_SPEED = 600;

const WEAPON_DAMAGE = 5;

const PLAYER_RADIUS = 50;
const BULLET_RADIUS = 10;

const mapW = 960;
const mapH = 640;

export default class DataManager extends Singleton {
  static get Instance() {
    return super.GetInstance<DataManager>();
  }

  //登陆数据
  myPlayerId = 1;

  //大厅数据
  roomInfo: IRoom;

  //游戏数据
  stage: Node;
  jm: JoyStickManager;
  prefabMap: Map<string, Prefab> = new Map();
  textureMap: Map<string, SpriteFrame[]> = new Map();
  actorMap: Map<number, ActorManager> = new Map();
  bulletMap: Map<number, BulletManager> = new Map();

  reset() {
    this.frameId = 0;
    this.stage = null;
    this.jm = null;
    this.actorMap.clear();
    this.bulletMap.clear();
    this.prefabMap.clear();
    this.textureMap.clear();
  }

  frameId = 0;
  lastState: IState;
  state: IState = {
    actors: [
      {
        id: 2,
        nickname: "哈哈1",
        position: {
          x: -200,
          y: -200,
        },
        direction: {
          x: 1,
          y: 0,
        },
        hp: 100,
        type: EntityTypeEnum.Actor1,
        weaponType: EntityTypeEnum.Weapon1,
        bulletType: EntityTypeEnum.Bullet1,
      },
      {
        id: 1,
        nickname: "哈哈2",
        position: {
          x: 200,
          y: 200,
        },
        direction: {
          x: 0,
          y: -1,
        },
        hp: 100,
        type: EntityTypeEnum.Actor2,
        weaponType: EntityTypeEnum.Weapon2,
        bulletType: EntityTypeEnum.Bullet2,
      },
    ],
    bullets: [],
    nextBulletId: 1,
  };

  applyInput(input: IClientInput) {
    switch (input.type) {
      case InputTypeEnum.ActorMove: {
        const {
          direction: { x, y },
          dt,
          id,
        } = input;
        const player = this.state.actors.find((e) => e.id === id);
        if (!player) {
          return;
        }

        player.position.x += toFixed(x * PLAYER_SPEED * dt);
        player.position.y += toFixed(y * PLAYER_SPEED * dt);

        player.position.x = clamp(player.position.x, -mapW / 2, mapW / 2);
        player.position.y = clamp(player.position.y, -mapH / 2, mapH / 2);

        player.direction = { x, y };
        break;
      }
      case InputTypeEnum.WeaponShoot: {
        const { owner, position, direction } = input;
        const bullet: IBullet = {
          id: this.state.nextBulletId++,
          owner,
          position,
          direction,
          type: this.actorMap.get(owner).bulletType,
        };
        this.state.bullets.push(bullet);

        EventManager.Instance.emit(EventEnum.BulletBorn, owner);
        break;
      }
      case InputTypeEnum.TimePast: {
        const { dt } = input;
        const { bullets, actors } = this.state;

        for (let i = bullets.length - 1; i >= 0; i--) {
          const bullet = bullets[i];
          for (let j = actors.length - 1; j >= 0; j--) {
            const player = actors[j];
            if ((player.position.x - bullet.position.x) ** 2 + (player.position.y - bullet.position.y) ** 2 < (PLAYER_RADIUS + BULLET_RADIUS) ** 2) {
              EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
                x: toFixed((player.position.x + bullet.position.x) / 2),
                y: toFixed((player.position.y + bullet.position.y) / 2),
              });

              player.hp -= WEAPON_DAMAGE;
              bullets.splice(i, 1);
              break;
            }
          }
          if (Math.abs(bullet.position.x) > mapW / 2 || Math.abs(bullet.position.y) > mapH / 2) {
            EventManager.Instance.emit(EventEnum.ExplosionBorn, bullet.id, {
              x: bullet.position.x,
              y: bullet.position.y,
            });
            bullets.splice(i, 1);
          }
        }

        for (const bullet of this.state.bullets) {
          bullet.position.x += toFixed(bullet.direction.x * BULLET_SPEED * dt);
          bullet.position.y += toFixed(bullet.direction.y * BULLET_SPEED * dt);
        }
      }
    }
  }
}
